﻿/*
 * Created by SharpDevelop.
 * User: Guy Fomi
 * Date: 2/6/2011
 * Time: 2:14 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using HtmlAgilityPack;
using System.Web.Script.Serialization;
using FileSystemDatabase;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TomatoSynchronizer.Entities;
using System.Text;
using System.IO;

namespace TomatoSynchronizer.Store
{
	/// <summary>
	/// Description of LanguageStore.
	/// </summary>
	public class LanguageStore : IStore<Language>
	{
		private HtmlWeb _client;
		private Site _site;
		public Site site {
			get { return _site; }
			set { _site = value; }
		}
		
		private JsonDatabase db;
		
		public LanguageStore()
		{
		}
		
		public LanguageStore(Site site,HtmlWeb client)
		{
			db = new JsonDatabase("sites/" + site.Name + "/Languages");
			_site = site;
			_client = client;
		}
		
		public Language GetItem(string LanguageId)
		{
			string url = _site.Url + "/admin/json.php?action=load_language&languages_id=" + LanguageId + "&module=languages";
			
			HtmlDocument doc = _client.Load(url);
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			
			try {
				JObject json = (JObject)JsonConvert.DeserializeObject(node.InnerText);
				foreach (JProperty property in json.Properties()) {
					if (property.Name == "data")
					{
						var val = property.Value.ToString();
						Language language = new Language(val);
						if (language != null)
						{
							language.TranslationGroups = GetTranslationGroups(language.languages_id);
							language.Store = this;
							return language;
						}
					}
				}
			} catch (JsonReaderException ex) {
//				return null;
				throw ex;
			}
			
			return null;
		}

		private bool InsertTranslation(Translation translation)
		{
			var post = new antiufo.HttpPost.HttpPostRequest(_site.Url + "/admin/json.php");
			post.AddField("action","add_translation");
			post.AddField("definition_group",translation.content_group);
			post.AddField("definition_key",translation.definition_key);
			post.AddField("languages_id",translation.languages_id);
			post.AddField("module","languages");
			post.AddField("definition_value",translation.definition_value);
			
			var resp = post.PostData();
			
			Stream s = resp.GetResponseStream();
			StreamReader sr = new StreamReader(s);

			var response = sr.ReadToEnd();
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			IDictionary json = serialiser.Deserialize<IDictionary>(response);
			if (json != null) {
				foreach (string key in json.Keys) {
					if (key == "success") {
						if(bool.Parse(json[key].ToString()))
						{
							return true;
						}
						
						return false;
					}
				}
			}
			
			throw new Exception("Could not insert the translations for the lhe language " + translation.languages_id + " in site " + _site.Name);
		}

		private bool UpdateTranslation(Translation translation)
		{
			var post = new antiufo.HttpPost.HttpPostRequest(_site.Url + "/admin/json.php");
			post.AddField("action","update_translation");
			post.AddField("group",translation.content_group);
			post.AddField("definition_key",translation.definition_key);
			post.AddField("languages_id",translation.languages_id);
			post.AddField("module","languages");
			post.AddField("definition_value",translation.definition_value);
			
			var resp = post.PostData();
			
			Stream s = resp.GetResponseStream();
			StreamReader sr = new StreamReader(s);

			var response = sr.ReadToEnd();
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			IDictionary json = serialiser.Deserialize<IDictionary>(response);
			if (json != null) {
				foreach (string key in json.Keys) {
					if (key == "success") {
						if(bool.Parse(json[key].ToString()))
						{
							return true;
						}
						
						return false;
					}
				}
			}
			
			throw new Exception("Unable to update Translation " + translation.definition_key);
		}
		
		public bool DeleteTranslation(Translation translation)
		{
			string url = _site.Url + "/admin/json.php?action=delete_translation&languages_definitions_id=" + translation.languages_definitions_id + "&module=languages";
			
			HtmlDocument doc = _client.Load(url);
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			IDictionary json = serialiser.Deserialize<IDictionary>(node.InnerText);
			if (json != null) {
				foreach (string key in json.Keys) {
					if (key == "success") {
						return bool.Parse(json[key].ToString());
					}
				}
			}
			
			throw new Exception("Unable to delete Translation " + translation.definition_key);
		}	
		
		public List<Language> GetItems()
		{
			db.Truncate();
			
			List<Language> Languages = new List<Language>();
			
			HtmlDocument doc = _client.Load(_site.Url + "/admin/json.php?action=list_languages&module=languages");
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			
			try
			{
				Dictionary<string,object> json = serialiser.Deserialize<Dictionary<string,object>>(node.InnerText);
				foreach(string key in json.Keys)
				{
					if(key == "records")
					{
						ArrayList recs = (ArrayList)json[key];
						for(int i = 0;i < recs.Count;i++)
						{
							Dictionary<string,object> record = (Dictionary<string,object>)recs[i];
							Language language = GetItem(record["languages_id"].ToString());
							Languages.Add(language);
						}
					}
				}
				
				foreach(Language lang in Languages)
				{
					PersistItem(lang);
				}
				
				PersistItems(Languages);
				
				return Languages;
				
			} catch (ArgumentException)
			{
				foreach(Language lang in Languages)
				{
					PersistItem(lang);
				}
				
				PersistItems(Languages);
				
				return Languages;
			}
		}
		
		private List<TranslationGroup> GetTranslationGroups(string LanguageId)
		{
			List<TranslationGroup> TranslationGroups = new List<TranslationGroup>();
			
			string url = _site.Url + "/admin/json.php?action=list_translation_groups&languages_id=" + LanguageId + "&module=languages&node=root";
			
			HtmlDocument doc = _client.Load(url);
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			string response = "{records:" + node.InnerText + "}";
			
			try
			{
				Dictionary<string,object> json = serialiser.Deserialize<Dictionary<string,object>>(response);
				foreach(string key in json.Keys)
				{
					if(key == "records")
					{
						ArrayList recs = (ArrayList)json[key];
						for(int i = 0;i < recs.Count;i++)
						{
							Dictionary<string,object> val = (Dictionary<string,object>)recs[i];
							TranslationGroup group = new TranslationGroup();
							group.id = val["id"].ToString();
							group.text = val["text"].ToString();
							group.Translations = GetTranslations(group.id,LanguageId);
							TranslationGroups.Add(group);
						}
					}
				}
				
				return TranslationGroups;
			}
			catch(Exception ex)
			{
				throw ex;
			}
		}

		private List<Translation> GetTranslations(string TranslationGroup,string LanguageId)
		{
			List<Translation> Translations = new List<Translation>();
			
			string url = _site.Url + "/admin/json.php?action=list_translations&group=" + TranslationGroup + "&languages_id=" + LanguageId + "&module=languages";
			
			HtmlDocument doc = _client.Load(url);
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			string response = "{records:" + node.InnerText + "}";
			Dictionary<string,object> json = serialiser.Deserialize<Dictionary<string,object>>(response);
			foreach(string key in json.Keys)
			{
				if(key == "records")
				{
					Dictionary<string,object> json1 = (Dictionary<string,object>)json[key];
					foreach(string cle in json1.Keys)
					{
						if(cle == "records")
						{
							ArrayList recs = (ArrayList)json1[cle];
							for(int i = 0;i < recs.Count;i++)
							{
								Dictionary<string,object> val = (Dictionary<string,object>)recs[i];
								Translation translation = new Translation();
								translation.content_group = val["content_group"].ToString();
								translation.definition_key = val["definition_key"].ToString();
								translation.definition_value = val["definition_value"].ToString();
								translation.languages_definitions_id = int.Parse(val["languages_definitions_id"].ToString());
								translation.languages_id = val["languages_id"].ToString();
								
								Translations.Add(translation);
							}
						}
					}
				}
			}
			
			return Translations;
		}
		
		private List<Dictionary<string,object>> GetParenLanguage(string LanguageId)
		{
			List<Dictionary<string,object>> entries = new List<Dictionary<string,object>>();
			string url = _site.Url + "/admin/json.php?action=get_parent_language&languages_id=" + LanguageId + "&module=languages";
			
			HtmlDocument doc = _client.Load(url);
			HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
			
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			Dictionary<string,object> json = serialiser.Deserialize<Dictionary<string,object>>(node.InnerText);
			foreach(string key in json.Keys)
			{
				if(key == "data")
				{
					Dictionary<string,object> Language = (Dictionary<string,object>)json[key];
					entries.Add(Language);
				}
				
				if(key == "records")
				{
					ArrayList recs = (ArrayList)json[key];
					for(int i = 0;i < recs.Count;i++)
					{
						Dictionary<string,object> Language = (Dictionary<string,object>)recs[i];
						entries.Add(Language);
					}
				}
			}
			
			return entries;
		}
		
		public void PersistItems(List<Language> items)
		{
			db.Write<List<Language>>(items,_site.Name);
		}
		
		public void Truncate()
		{
			db.Truncate();
		}
		
		public void PersistItem(Language item)
		{
			db.Write<Language>(item,item.languages_id);
		}
		
		public bool MoveItem(string itemId, string folderId)
		{
			throw new NotImplementedException();
		}
		
		public bool DeleteItem(string itemId)
		{
			Language language = LoadItem(itemId);
			if(language != null)
			{
				if(language.is_default)
				{
					//we cannot delete a default language, so we have to update another language as default
					var updated = false;
					var languages = LoadItems();
					foreach(Language lang in languages)
					{
						if(lang.code != language.code)
						{
							lang.is_default = true;
							UpdateItem(lang);
							updated = true;
						}
					}
					if(!updated)
					{
						//we have only one default language, we cannot delete it
						return false;
					}
				}
				else
				{
					HtmlDocument doc = _client.Load(_site.Url + "/admin/json.php?action=delete_language&code=" + language.code + "&languages_id=" + itemId + "&module=languages");

					HtmlNode node = doc.DocumentNode.SelectSingleNode("/");
					JavaScriptSerializer serialiser = new JavaScriptSerializer();
					IDictionary json = serialiser.Deserialize<IDictionary>(node.InnerText);
					foreach (string key in json.Keys) {
						if (key == "success") {
							var response = bool.Parse(json[key].ToString());
							if(response)
							{
								db.Delete<Language>(itemId);
							}
							
							return response;
						}
					}

					return false;
				}
			}
			
			return true;
		}
		
		public Language[] LoadItems()
		{
			Language[] languages = db.Read<Language>();
			foreach(Language language in languages)
			{
				language.Store = this;
			}
			
			return languages;
		}
		
		public Language LoadItem(string itemId)
		{
			Language item = db.Read<Language>(itemId);
			if(item != null)
			{
				return item;
			}
			
			return null;
		}
		
		public bool Contains(Language item)
		{
			var languages = LoadItems();
			
			foreach(Language language in languages)
			{
				if(language.code == item.code)
				{
					return true;
				}
			}
			
			return false;
		}
		
		public bool UpdateItem(Language item)
		{
			CookieContainer cookies = new CookieContainer();
			NameValueCollection querystring = new NameValueCollection();
			
			querystring["locale"] = item.locale;
			querystring["languages_id"] = item.languages_id	;
			querystring["date_format_short"] = item.date_format_short;
			querystring["date_format_long"] = item.date_format_long;
			querystring["currencies_id"] = item.currencies_id;
			querystring["charset"] = item.charset;
			querystring["code"] = item.code;
			querystring["action"] = "save_language";
			querystring["module"] = "languages";
			querystring["name"] = item.name	;
			querystring["numeric_separator_decimal"] = item.numeric_separator_decimal;
			querystring["numeric_separator_thousands"] = item.numeric_separator_thousands;
			querystring["parent_id"] = item.parent_id;
			querystring["sort_order"] = item.sort_order;
			querystring["text_id"] = item.text_direction;
			querystring["time_format"] = item.time_format;
			
			if(item.is_default)
			{
				querystring["default"] = "on";
			}
			
			string response = Utility.UploadFileEx("dummy.txt", _site.Url + "/admin/json.php", "language", "text/html", querystring, cookies);
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			IDictionary json = serialiser.Deserialize<IDictionary>(response);
			if (json != null) {
				foreach (string key in json.Keys) {
					if (key == "success") {
						if(bool.Parse(json[key].ToString()))
						{
							var languages = GetItems();
							
							foreach(Language language in languages)
							{
								if(language.code == item.code)
								{
									//must be the new language
									//we update the translations
									
									bool sync = true;
									
//									sync = TruncateTranslations(language);
									if(sync)
									{
										foreach(TranslationGroup group in item.TranslationGroups)
										{
											foreach(Translation translation in group.Translations)
											{
												translation.languages_id = item.languages_id;
												
												var trans = language.LoadTranslation(group.id,translation.definition_key);
												
												if(trans != null)
												{
													if(!trans.Equals(translation))
													{
														sync = UpdateTranslation(translation);
														if(!sync)
														{
															throw new Exception("Could not update the translations for the lhe language " + item.name + " in site " + _site.Name);
														}
													}
												}
												else
												{
													sync = InsertTranslation(translation);
													if(!sync)
													{
														throw new Exception("Could not insert the translations for the lhe language " + item.name + " in site " + _site.Name);
													}
												}
											}
										}
									}
								}
							}
							
							return true;
						}
						
						return false;
					}
				}
			}

			//could not update the language
			throw new Exception("The language " + item.name + " has not been updated");
		}
		
		public bool TruncateTranslations(Language language)
		{
			foreach(TranslationGroup group in language.TranslationGroups)
			{
				foreach(Translation translation in group.Translations)
				{
					if(!DeleteTranslation(translation))
					{
						return false;
					}
				}
			}
			
			return true;
		}
		
		public string InsertItem(Language item)
		{
			CookieContainer cookies = new CookieContainer();
			NameValueCollection querystring = new NameValueCollection();
			
			querystring["module"] = "languages";
			querystring["languages_id"] = item.code;
			querystring["import_type"] = "replace";
			querystring["action"] = "import_language";

			retry :
				string response = Utility.UploadFileEx("dummy.txt", _site.Url + "/admin/json.php", "language", "text/html", querystring, cookies);
			JavaScriptSerializer serialiser = new JavaScriptSerializer();
			IDictionary json = serialiser.Deserialize<IDictionary>(response);
			if (json != null) {
				foreach (string key in json.Keys) {
					if (key == "success") {
						if(bool.Parse(json[key].ToString()))
						{
							//get the new Language id
							var languages = GetItems();
							
							foreach(Language language in languages)
							{
								if(language.code == item.code)
								{
									bool sync = true;
									
									if(sync)
									{
										foreach(TranslationGroup group in item.TranslationGroups)
										{
											foreach(Translation translation in group.Translations)
											{
												translation.languages_id = item.languages_id;
												
												var trans = language.LoadTranslation(group.id,translation.definition_key);
												
												if(trans != null)
												{
													if(!trans.Equals(translation))
													{
														sync = UpdateTranslation(translation);
													}
												}
												else
												{
													sync = InsertTranslation(translation);
												}
											}
										}
										
										if(sync)
										{
											return language.languages_id;
										}
										
										throw new Exception("Could not update the translations for the lhe language " + item.name + " in site " + _site.Name);
									}
									
									throw new Exception("Could not delete the tranlations for language " + language.name);
								}
							}
						}
						else
						{
							goto retry;
						}
					}										
				}
			}

			//could not insert the language
			throw new Exception("Unable to insert language " + item.name);
		}
	}
}
